package k3s

import (
	"bytes"
	"context"
	_ "embed"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"text/template"
	"time"

	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/mount"
	"github.com/docker/go-connections/nat"
	"gopkg.openfuyao.cn/bkecommon"
	configinit "gopkg.openfuyao.cn/bkecommon/cluster/initialize"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	econd "gopkg.openfuyao.cn/bkeadm/pkg/executor/containerd"
	"gopkg.openfuyao.cn/bkeadm/pkg/executor/docker"
	"gopkg.openfuyao.cn/bkeadm/pkg/executor/exec"
	"gopkg.openfuyao.cn/bkeadm/pkg/executor/k8s"
	"gopkg.openfuyao.cn/bkeadm/pkg/global"
	"gopkg.openfuyao.cn/bkeadm/utils"
	"gopkg.openfuyao.cn/bkeadm/utils/log"
)

var (
	//go:embed registries.yaml
	registries string
	//go:embed generate-custom-ca-certs.sh
	k3sCertScript string
	//go:embed core.conf
	k3sCoreScript string
	k3sImage      = utils.DefaultLocalK3sRegistry
	k3sPause      = utils.DefaultK3sPause
	k3sCoredns    string
)

const (
	// DefaultK3sDataDir k3s证书相关数据路径
	DefaultK3sDataDir  = "/var/lib/rancher/k3s"
	waitConfigInterval = 2
	waitInterval       = 5   // 每次检查的间隔（5秒）
	waitTimeout        = 300 // 总超时时间（5分钟）
)

func EnsureDirExists(dir string) error {
	if !utils.Exists(dir) {
		err := os.MkdirAll(dir, utils.DefaultDirPermission)
		if err != nil {
			return err
		}
	}
	return nil
}

func StartK3sWithContainerd(otherRepo, otherRepoIp, hostIP, imageRepo, imageRepoPort, kubernetesPort string) error {
	if isKubernetesAvailable() {
		log.BKEFormat(log.INFO, "A kubernetes cluster already exists.")
		return nil
	}
	if otherRepo != "" {
		k3sImage = fmt.Sprintf("%s%s", otherRepo, k3sImage)
		k3sPause = fmt.Sprintf("%s%s", otherRepo, k3sPause)
	} else {
		k3sImage = fmt.Sprintf("0.0.0.0:%s/%s/%s", imageRepoPort, bkecommon.ImageRegistryKubernetes, k3sImage)
		k3sPause = fmt.Sprintf("%s:443/%s/%s", imageRepo, bkecommon.ImageRegistryKubernetes, k3sPause)
	}
	err := econd.EnsureImageExists(k3sImage)
	if err != nil {
		return err
	}
	_ = econd.ContainerRemove(utils.LocalKubernetesName)
	// step.-1 set ks3 config file
	k3sConfig := "/etc/rancher/k3s"
	if !utils.Exists(k3sConfig) {
		err = os.MkdirAll(k3sConfig, 0644)
		if err != nil {
			return err
		}
	}
	err = customCA()
	if err != nil {
		return err
	}
	// step.0 Gets the mirror repository address
	repo := fmt.Sprintf("%s:%s", imageRepo, "443")
	imageRepoIP := hostIP
	log.Infof(
		"参数值：otherRepo=%s, otherRepoIp=%s, hostIP=%s, imageRepo=%s, imageRepoPort=%s, kubernetesPort=%s",
		otherRepo,
		otherRepoIp,
		hostIP,
		imageRepo,
		imageRepoPort,
		kubernetesPort,
	)
	//  在线，判断是否是domain对应的镜像仓
	//if otherRepo != "" && strings.Contains(otherRepo, configinit.DefaultImageRepo) {
	if otherRepo != "" && strings.Contains(otherRepo, imageRepo) {
		imageRepoIP = otherRepoIp
		repo = strings.Split(otherRepo, "/")[0]
	} else {
		imageRepoInfo, err := econd.ContainerInspect(utils.LocalImageRegistryName)
		if err == nil && len(imageRepoInfo.Id) > 0 {
			imageRepoIP = imageRepoInfo.NetworkSettings.IPAddress
		}
	}
	tmpl0, err := template.New("registries").Parse(registries)
	if err != nil {
		return err
	}
	buf0 := new(bytes.Buffer)
	err = tmpl0.Execute(buf0, map[string]string{"repo": repo})
	if err != nil {
		return err
	}
	_ = os.Remove(k3sConfig + "/registries.yaml")
	err = os.WriteFile(k3sConfig+"/registries.yaml", buf0.Bytes(), 0644)
	if err != nil {
		return err
	}
	log.BKEFormat(log.INFO, "Start the local Kubernetes cluster...")
	// step.1 start container
	k3sStartScript := []string{"run", "-d", fmt.Sprintf("--name=%s", utils.LocalKubernetesName),
		"-p", fmt.Sprintf("%s:6443", kubernetesPort), "--privileged", "--restart=always", "-p", "30010:30010",
		"--add-host", fmt.Sprintf("%s:%s", imageRepo, imageRepoIP),
		"-v", "/etc/rancher/k3s:/etc/rancher/k3s", "-v", "/etc/timezone:/etc/timezone", "-v", "/etc/docker:/etc/docker",
		"-v", "/etc/localtime:/etc/localtime", "-v", "/var/lib/rancher/k3s:/var/lib/rancher/k3s", "-v", "/bke:/bke",
		"-v", "/etc/openFuyao:/etc/openFuyao", "-v", fmt.Sprintf("%s:%s", utils.DefaultExtendManifestsDir, utils.DefaultExtendManifestsDir),
		k3sImage, "server", "--snapshotter=native",
		"--service-cidr=100.10.0.0/16", "--cluster-cidr=100.20.0.0/16", "--token=e65832d9d955473260d9247e7dd2879c",
		fmt.Sprintf("--tls-san=%s", hostIP), fmt.Sprintf("--node-name=%s", utils.LocalKubernetesName),
		fmt.Sprintf("--pause-image=%s", k3sPause),
		"--disable=servicelb,traefik,local-storage,metrics-server"}

	err = econd.Run(k3sStartScript)
	if err != nil {
		return err
	}
	time.Sleep(2 * time.Second)

	// step.5 Copy kubectl to the node
	err = econd.CP(fmt.Sprintf("%s:/bin/k3s", utils.LocalKubernetesName), "/usr/bin/kubectl")
	if err != nil {
		log.BKEFormat(log.ERROR, "Failed to copy kubectl from the container")
		return err
	}
	// step.6 Process kubeconfig to $HOME/.kube/config
	result := []byte{}
	for i := 0; i < 5; i++ {
		result, err = os.ReadFile("/etc/rancher/k3s/k3s.yaml")
		if err != nil {
			time.Sleep(2 * time.Second)
			log.BKEFormat(log.WARN, "Failed to get kubeconfig, retrying...")
			continue
		}
	}
	if len(result) == 0 {
		return errors.New("Failed to get /etc/rancher/k3s/k3s.yaml ")
	}
	home, _ := os.UserHomeDir()
	_ = os.MkdirAll(fmt.Sprintf("%s/.kube", home), 0755)
	kubeconfigPath := fmt.Sprintf(fmt.Sprintf("%s/.kube/config", home))
	kubeconfigContent := strings.Replace(string(result), "127.0.0.1:6443", fmt.Sprintf("%s:%s", hostIP, kubernetesPort), 1)
	err = os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0600)
	if err != nil {
		return err
	}
	_ = os.Remove("/etc/rancher/k3s/k3s.yaml")
	err = os.WriteFile("/etc/rancher/k3s/k3s.yaml", []byte(kubeconfigContent), 0644)
	if err != nil {
		log.BKEFormat(log.WARN, fmt.Sprintf("Failed to rename k3s.yaml, Please Run export KUBECONFIG=%s", kubeconfigPath))
	}
	// step.7 Check whether K8S is accessible
	log.BKEFormat(log.INFO, "Waiting for the cluster to start...")
	for i := 1; i < 10; i++ {
		global.K8s, err = k8s.NewKubernetesClient(kubeconfigPath)
		if err != nil {
			time.Sleep(6 * time.Second)
			continue
		}
		break
	}
	if global.K8s == nil {
		return errors.New("Failed to connect to Kubernetes. ")
	}
	// step.8 wait node ready
	log.BKEFormat(log.INFO, "Waiting for cluster Ready...")
	clientset := global.K8s.GetClient()
	for i := 0; i < 10; i++ {
		node, err := clientset.CoreV1().Nodes().Get(context.Background(), utils.LocalKubernetesName, metav1.GetOptions{})
		if err != nil {
			time.Sleep(3 * time.Second)
			continue
		}
		if len(node.Spec.Taints) > 1 {
			time.Sleep(3 * time.Second)
			continue
		}
		_, err = clientset.CoreV1().Namespaces().Get(context.Background(), "kube-system", metav1.GetOptions{})
		if err != nil {
			time.Sleep(3 * time.Second)
			continue
		}
		break
	}
	// step.9 Create Kubeconfig Secret in the Kubernetes cluster
	_, err = clientset.CoreV1().Secrets(metav1.NamespaceSystem).Create(context.Background(), &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "localkubeconfig",
			Namespace: metav1.NamespaceSystem,
		},
		StringData: map[string]string{
			"config": kubeconfigContent,
		},
	}, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	log.BKEFormat(log.INFO, "The local Kubernetes startup succeeded")
	return nil
}

func checkCurrentCoreDNSConfig() error {
	cmd := exec.CommandExecutor{}

	// 获取当前的CoreDNS ConfigMap
	getCmd := []string{"get", "configmap", "coredns", "-n", "kube-system", "-o", "jsonpath={.data.Corefile}"}
	output, err := cmd.ExecuteCommandWithCombinedOutput(utils.KubeCtl, getCmd...)
	if err != nil {
		return fmt.Errorf("failed to get current coredns config: %v", err)
	}

	log.BKEFormat(log.INFO, "Current CoreDNS configuration detected")

	// 检查是否已经包含/etc/resolv.conf
	if strings.Contains(output, "/etc/resolv.conf") {
		log.BKEFormat(log.INFO, "Found /etc/resolv.conf in current config, will be replaced")
		return nil
	}

	// 检查是否已经使用固定DNS
	if containsFixedDNS(output) {
		log.BKEFormat(log.INFO, "Fixed DNS already configured, no need to patch")
		return nil
	}

	log.BKEFormat(log.INFO, "Current config does not contain /etc/resolv.conf")
	return nil
}

func verifyCoreDNSConfig() error {
	cmd := exec.CommandExecutor{}

	// 等待一段时间让配置生效
	time.Sleep(time.Duration(waitConfigInterval) * time.Second)

	// 获取修改后的配置进行验证
	getCmd := []string{"get", "configmap", "coredns", "-n", "kube-system", "-o", "jsonpath={.data.Corefile}"}
	output, err := cmd.ExecuteCommandWithCombinedOutput(utils.KubeCtl, getCmd...)
	if err != nil {
		return fmt.Errorf("failed to verify coredns config: %v", err)
	}

	// 检查是否仍然包含/etc/resolv.conf
	if strings.Contains(output, "/etc/resolv.conf") {
		return fmt.Errorf("config still contains /etc/resolv.conf after patch")
	}

	// 检查是否成功配置了固定DNS
	if !containsFixedDNS(output) {
		return fmt.Errorf("fixed DNS not found in config after patch")
	}

	log.BKEFormat(log.INFO, "CoreDNS config verification passed: fixed DNS configured successfully")
	return nil
}

func verifyCoreDNSRunning() error {
	cmd := exec.CommandExecutor{}

	// 等待Pod重启
	time.Sleep(time.Duration(waitInterval) * time.Second)

	// 检查CoreDNS Pod状态
	checkCmd := []string{"get", "pods", "-n", "kube-system", "-l", "k8s-app=kube-dns", "-o", "jsonpath={.items[*].status.phase}"}
	output, err := cmd.ExecuteCommandWithCombinedOutput(utils.KubeCtl, checkCmd...)
	if err != nil {
		return fmt.Errorf("failed to check coredns pod status: %v", err)
	}

	if !strings.Contains(output, "Running") {
		return fmt.Errorf("coredns pods not in Running state: %s", output)
	}

	log.BKEFormat(log.INFO, "CoreDNS pods are running successfully")
	return nil
}

// 检查是否包含固定DNS配置
func containsFixedDNS(config string) bool {
	fixedDNSPatterns := []string{
		"forward . 8.8.8.8",
		"forward . 8.8.4.4",
		"forward . 1.1.1.1",
		"forward . 1.0.0.1",
		"forward . 208.67.222.222",
		"forward . 208.67.220.220",
	}

	for _, pattern := range fixedDNSPatterns {
		if strings.Contains(config, pattern) {
			return true
		}
	}
	return false
}

func FixCoreDnsLoop(otherRepo, imageRepo string) error {
	if err := checkCurrentCoreDNSConfig(); err != nil {
		return fmt.Errorf("coreDNS config check failed: %v", err)
	}

	escapedCorefile := strings.ReplaceAll(k3sCoreScript, "\n", "\\n")
	escapedCorefile = strings.ReplaceAll(escapedCorefile, "\"", "\\\"")
	patchData := fmt.Sprintf(`{"data":{"Corefile":"%s"}}`, escapedCorefile)

	cmd := exec.CommandExecutor{}
	patchCmd := []string{"patch", "configmap", "coredns", "-n", "kube-system", "--type", "merge", "-p", patchData}
	output, err := cmd.ExecuteCommandWithCombinedOutput(utils.KubeCtl, patchCmd...)
	if err != nil {
		return fmt.Errorf("failed to patch coredns: %v, output: %s", err, output)
	}
	log.BKEFormat(log.INFO, fmt.Sprintf("CoreDNS ConfigMap updated: %s", output))

	// 验证配置是否成功修改
	if err := verifyCoreDNSConfig(); err != nil {
		return fmt.Errorf("config verification failed after patch: %v", err)
	}

	if err := ModK3sCorednsImage(otherRepo, imageRepo); err != nil {
		return fmt.Errorf("mod coredns image tag failed: %v", err)
	}
	deleteCmd := []string{"delete", "pod", "-n", "kube-system", "-l", "k8s-app=kube-dns"}
	output, err = cmd.ExecuteCommandWithCombinedOutput(utils.KubeCtl, deleteCmd...)
	if err != nil {
		return fmt.Errorf("failed to delete coredns pods: %v, output: %s", err, output)
	}
	log.BKEFormat(log.INFO, fmt.Sprintf("CoreDNS pods restarted: %s", output))

	// 最终验证
	if err := verifyCoreDNSRunning(); err != nil {
		return fmt.Errorf("coreDNS not running properly after restart: %v", err)
	}

	return nil
}

func ModCorednsConfigWithRetry(otherRepo, imageRepo string) error {
	const maxRetries = 3
	var lastError error

	for i := 0; i < maxRetries; i++ {
		log.BKEFormat(log.INFO, fmt.Sprintf("Attempting to fix CoreDNS loop (attempt %d/%d)", i+1, maxRetries))

		if err := FixCoreDnsLoop(otherRepo, imageRepo); err != nil {
			lastError = err
			log.BKEFormat(log.WARN, fmt.Sprintf("Attempt %d failed: %v", i+1, err))
			time.Sleep(time.Duration(i+1) * waitConfigInterval * time.Second) // 指数退避
			continue
		}

		log.BKEFormat(log.INFO, fmt.Sprintf("CoreDNS loop fix completed successfully on attempt %d", i+1))
		return nil
	}

	return fmt.Errorf("failed to fix CoreDNS loop after %d attempts: %v", maxRetries, lastError)
}

func ModK3sCorednsImage(otherRepo, imageRepo string) error {
	log.BKEFormat(log.INFO, fmt.Sprintf("等待 coredns 就绪（超时%d秒，每%d秒检查一次）", waitTimeout, waitInterval))
	startTime := time.Now()
	cmdExecutor := exec.CommandExecutor{} // 复用命令执行器
	for {
		// 1. 检查coredns Deployment是否存在且就绪
		checkCmd := []string{
			"exec", utils.LocalKubernetesName, "kubectl",
			"-n", "kube-system",
			"get", "deployment/coredns",
			"-o", `jsonpath='{.status.conditions[?(@.type=="Available")].status}'`, // 只取Available状态
			"--ignore-not-found", // 不存在时不报错
		}
		output, err := cmdExecutor.ExecuteCommandWithCombinedOutput(utils.NerdCtl, checkCmd...)
		outputStr := strings.Trim(string(output), "'") // 去除jsonpath返回的单引号
		// 2. 判断是否满足条件：输出为"True"表示存在且就绪,
		if outputStr == "True" {
			log.BKEFormat(log.INFO, fmt.Sprintf("coredns 已就绪（耗时：%v）", time.Since(startTime).Round(time.Second)))
			break
		}
		// 3. 判断是否超时
		if time.Since(startTime) >= time.Duration(waitTimeout)*time.Second {
			errMsg := fmt.Sprintf("等待超时（%ds）：coredns仍未就绪，检查输出：%s，错误：%v",
				waitTimeout, outputStr, err)
			log.BKEFormat(log.ERROR, errMsg)
			return fmt.Errorf(errMsg)
		}
		// 4. 未满足条件且未超时，继续等待
		log.BKEFormat(log.INFO, fmt.Sprintf("coredns 未就绪（当前状态：%s），继续等待...", outputStr))
		time.Sleep(time.Duration(waitInterval) * time.Second)
	}
	if otherRepo != "" {
		if imageRepo == configinit.DefaultImageRepo { //是默认在线安装
			k3sCoredns = fmt.Sprintf("%s%s", "cr.openfuyao.cn/openfuyao/", "kubernetes/coredns:v1.10.1")
		} else {
			log.Infof("coredns使用的镜像仓是：%s", otherRepo)
			k3sCoredns = fmt.Sprintf("%s%s", otherRepo, "kubernetes/coredns:v1.10.1")
		}
	} else {
		k3sCoredns = fmt.Sprintf("%s:443/%s/%s", imageRepo, bkecommon.ImageRegistryKubernetes, "kubernetes/coredns:v1.10.1")
	}
	// Modify coredns images
	k3sModCorednsImageScript := []string{"exec", utils.LocalKubernetesName, "kubectl",
		"-n", "kube-system", "set", "image", "deployment/coredns", fmt.Sprintf("coredns=%s", k3sCoredns)}
	log.BKEFormat(log.INFO, fmt.Sprintf("生成的 coredns 镜像地址: %s", k3sCoredns))
	var cmd2 = exec.CommandExecutor{}
	output, err := cmd2.ExecuteCommandWithCombinedOutput(utils.NerdCtl, k3sModCorednsImageScript...)
	if err != nil {
		log.BKEFormat(log.ERROR, fmt.Sprintf("执行命令失败: %v, 命令: %v, 输出: %s",
			err, k3sModCorednsImageScript, string(output)))
		return err
	}
	log.BKEFormat(log.INFO, "mod k3s coredns image tag succeeded")
	return nil
}

func isKubernetesAvailable() bool {
	var err error
	global.K8s, err = k8s.NewKubernetesClient("")
	if err != nil {
		return false
	}
	v, _ := global.K8s.GetClient().ServerVersion()
	if v == nil {
		return false
	}
	clientset := global.K8s.GetClient()
	nodes, err := clientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
	if err != nil {
		return false
	}
	for _, node := range nodes.Items {
		for _, cond := range node.Status.Conditions {
			if cond.Type == corev1.NodeReady && cond.Status == corev1.ConditionTrue {
				return true
			}
		}
	}
	return false
}

func StartK3sWithDocker(otherRepo, otherRepoIp, hostIP, imageRepo, imageRepoPort, kubernetesPort string) error {
	if isKubernetesAvailable() {
		log.BKEFormat(log.INFO, "A kubernetes cluster already exists.")
		return nil
	}
	if otherRepo != "" {
		k3sImage = fmt.Sprintf("%s%s", otherRepo, k3sImage)
		k3sPause = fmt.Sprintf("%s%s", otherRepo, k3sPause)
	} else {
		k3sImage = fmt.Sprintf("0.0.0.0:%s/%s/%s", imageRepoPort, bkecommon.ImageRegistryKubernetes, k3sImage)
		k3sPause = fmt.Sprintf("%s:443/%s/%s", imageRepo, bkecommon.ImageRegistryKubernetes, k3sPause)
	}
	err := global.Docker.EnsureImageExists(docker.ImageRef{Image: k3sImage}, utils.RetryOptions{MaxRetry: 3, Delay: 1})
	if err != nil {
		return err
	}
	containerRunFlag, err := global.Docker.EnsureContainerRun(utils.LocalKubernetesName)
	if err != nil {
		return err
	}
	// The container is already running
	if containerRunFlag {
		if isKubernetesAvailable() {
			log.BKEFormat(log.INFO, "The local Kubernetes cluster is already running")
			return nil
		}
		_ = global.Docker.ContainerRemove(utils.LocalKubernetesName)
	}
	// step.-1 set ks3 config file
	k3sConfig := "/etc/rancher/k3s"
	if !utils.Exists(k3sConfig) {
		err = os.MkdirAll(k3sConfig, 0644)
		if err != nil {
			return err
		}
	}
	err = customCA()
	if err != nil {
		return err
	}
	// step.0 Gets the mirror repository address
	repo := fmt.Sprintf("%s:%s", imageRepo, "443")
	imageRepoIP := hostIP
	if otherRepo != "" && strings.Contains(otherRepo, configinit.DefaultImageRepo) {
		imageRepoIP = otherRepoIp
		repo = strings.Split(otherRepo, "/")[0]
	} else {
		client := global.Docker.GetClient()
		imageRepoInfo, err := client.ContainerInspect(context.Background(), utils.LocalImageRegistryName)
		if err == nil {
			imageRepoIP = imageRepoInfo.NetworkSettings.IPAddress
		}
	}
	tmpl0, err := template.New("registries").Parse(registries)
	if err != nil {
		return err
	}
	buf0 := new(bytes.Buffer)
	err = tmpl0.Execute(buf0, map[string]string{"repo": repo})
	if err != nil {
		return err
	}
	_ = os.Remove(k3sConfig + "/registries.yaml")
	err = os.WriteFile(k3sConfig+"/registries.yaml", buf0.Bytes(), 0644)
	if err != nil {
		return err
	}
	log.BKEFormat(log.INFO, "Start the local Kubernetes cluster...")
	// step.1 start container
	initFlag := false
	err = global.Docker.Run(&container.Config{
		Hostname:     utils.LocalKubernetesName,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		ExposedPorts: map[nat.Port]struct{}{
			"6443/tcp": {},
		},
		Tty:       true,
		StdinOnce: false,
		Env:       []string{"KUBECONFIG=/etc/kubernetes/admin.conf"},
		Cmd: []string{"server", "--snapshotter=native", "--service-cidr=100.10.0.0/16",
			"--cluster-cidr=100.20.0.0/16", "--token=e65832d9d955473260d9247e7dd2879c",
			fmt.Sprintf("--tls-san=%s", hostIP),
			fmt.Sprintf("--node-name=%s", utils.LocalKubernetesName),
			fmt.Sprintf("--pause-image=%s", k3sPause),
			"--disable=coredns,servicelb,traefik,local-storage,metrics-server"},
		Image: k3sImage,
		Volumes: map[string]struct{}{
			"/var": {},
		},
		Labels:     map[string]string{"bke-local-kubernetes": "cluster-api"},
		StopSignal: "SIGRTMIN+3",
	}, &container.HostConfig{
		PortBindings: map[nat.Port][]nat.PortBinding{
			nat.Port("6443/tcp"): {
				{
					HostIP:   "0.0.0.0",
					HostPort: kubernetesPort,
				},
			},
		},
		RestartPolicy: container.RestartPolicy{
			Name:              "on-failure",
			MaximumRetryCount: 10,
		},
		ExtraHosts:  []string{fmt.Sprintf("%s:%s", imageRepo, imageRepoIP)},
		Privileged:  true,
		SecurityOpt: []string{"seccomp=unconfined", "apparmor=unconfined", "label=disable"},
		Tmpfs: map[string]string{
			"/run": "",
			"/tmp": "",
		},
		Mounts: []mount.Mount{
			{
				Type:   mount.TypeBind,
				Source: "/etc/rancher/k3s",
				Target: "/etc/rancher/k3s",
			},
			{
				Type:   mount.TypeBind,
				Source: "/var/lib/rancher/k3s",
				Target: "/var/lib/rancher/k3s",
			},
			{
				Type:     mount.TypeBind,
				Source:   "/etc/timezone",
				Target:   "/etc/timezone",
				ReadOnly: true,
			},
			{
				Type:     mount.TypeBind,
				Source:   "/etc/localtime",
				Target:   "/etc/localtime",
				ReadOnly: true,
			},
		},
		Init: &initFlag,
	}, nil, nil, utils.LocalKubernetesName)
	if err != nil {
		return err
	}

	time.Sleep(2 * time.Second)
	// step.5 Copy kubectl to the node
	err = global.Docker.CopyFromContainer(utils.LocalKubernetesName, "/bin/k3s", "/usr/bin/kubectl")
	if err != nil {
		log.BKEFormat(log.ERROR, "Failed to copy kubectl from the container")
		return err
	}
	// step.6 Process kubeconfig to $HOME/.kube/config
	result := []byte{}
	for i := 0; i < 5; i++ {
		result, err = os.ReadFile("/etc/rancher/k3s/k3s.yaml")
		if err != nil {
			time.Sleep(2 * time.Second)
			log.BKEFormat(log.WARN, "Failed to get kubeconfig, retrying...")
			continue
		}
	}
	if len(result) == 0 {
		return errors.New("Failed to get /etc/rancher/k3s/k3s.yaml ")
	}
	home, _ := os.UserHomeDir()
	_ = os.MkdirAll(fmt.Sprintf("%s/.kube", home), 0755)
	kubeconfigPath := fmt.Sprintf(fmt.Sprintf("%s/.kube/config", home))
	kubeconfigContent := strings.Replace(string(result), "127.0.0.1:6443", fmt.Sprintf("%s:%s", hostIP, kubernetesPort), 1)
	err = os.WriteFile(kubeconfigPath, []byte(kubeconfigContent), 0600)
	if err != nil {
		return err
	}
	_ = os.Remove("/etc/rancher/k3s/k3s.yaml")
	err = os.WriteFile("/etc/rancher/k3s/k3s.yaml", []byte(kubeconfigContent), 0644)
	if err != nil {
		log.BKEFormat(log.WARN, fmt.Sprintf("Failed to rename k3s.yaml, Please Run export KUBECONFIG=%s", kubeconfigPath))
	}
	// step.7 Check whether K8S is accessible
	log.BKEFormat(log.INFO, "Waiting for the cluster to start...")
	for i := 1; i < 10; i++ {
		global.K8s, err = k8s.NewKubernetesClient(kubeconfigPath)
		if err != nil {
			time.Sleep(6 * time.Second)
			continue
		}
		break
	}
	if global.K8s == nil {
		return errors.New("Failed to connect to Kubernetes. ")
	}
	// step.8 wait node ready
	log.BKEFormat(log.INFO, "Waiting for cluster Ready...")
	clientset := global.K8s.GetClient()
	for i := 0; i < 10; i++ {
		node, err := clientset.CoreV1().Nodes().Get(context.Background(), utils.LocalKubernetesName, metav1.GetOptions{})
		if err != nil {
			time.Sleep(3 * time.Second)
			continue
		}
		if len(node.Spec.Taints) > 1 {
			time.Sleep(3 * time.Second)
			continue
		}
		_, err = clientset.CoreV1().Namespaces().Get(context.Background(), "kube-system", metav1.GetOptions{})
		if err != nil {
			time.Sleep(3 * time.Second)
			continue
		}
		break
	}
	// step.9 Create Kubeconfig Secret in the Kubernetes cluster
	_, err = clientset.CoreV1().Secrets(metav1.NamespaceSystem).Create(context.Background(), &corev1.Secret{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "localkubeconfig",
			Namespace: metav1.NamespaceSystem,
		},
		StringData: map[string]string{
			"config": kubeconfigContent,
		},
	}, metav1.CreateOptions{})
	if err != nil {
		return err
	}
	log.BKEFormat(log.INFO, "The local Kubernetes startup succeeded")
	return nil
}

func customCA() error {
	var (
		output string
		err    error
	)
	err = os.RemoveAll(DefaultK3sDataDir)
	if err != nil {
		log.BKEFormat(log.ERROR, fmt.Sprintf("Unable to delete %s file %s", DefaultK3sDataDir, err.Error()))
	}
	// save generate-custom-ca-certs.sh to /var/lib/rancher/k3s/generate-custom-ca-certs.sh
	if !utils.Exists(DefaultK3sDataDir) {
		err = os.MkdirAll(DefaultK3sDataDir, utils.DefaultDirPermission)
		if err != nil {
			return fmt.Errorf("create k3s certs dir failed: %w", err)
		}
	}
	genShFile := filepath.Join(DefaultK3sDataDir, "generate-custom-ca-certs.sh")
	err = os.WriteFile(genShFile, []byte(k3sCertScript), utils.DefaultFilePermission)
	if err != nil {
		return fmt.Errorf("write generate-custom-ca-certs.sh failed: %w", err)
	}

	executor := &exec.CommandExecutor{}
	output, err = executor.ExecuteCommandWithCombinedOutput("/bin/bash", "-c",
		fmt.Sprintf("cd %s && chmod +x ./generate-custom-ca-certs.sh && ./generate-custom-ca-certs.sh &&"+
			"chmod -x ./generate-custom-ca-certs.sh", DefaultK3sDataDir))
	if err != nil {
		return fmt.Errorf("generate k3s tls cert failed, output: %s, err: %w", output, err)
	}

	return nil
}
